#include "autoload.hpp"
#include <fstream>
#include <getopt.h>
#include <iostream>
#include <regex>
#include <unordered_map>

using namespace std;
static regex reg("^([A-Z]+)=(.*)");

static std::unordered_map<string, string> get_envmap(char** envp)
{
    std::unordered_map<std::string, std::string> envmap;

    for (char** env = envp; *env != 0; env++) {
        char*  thisEnv = *env;
        cmatch cm;
        if (regex_match(thisEnv, cm, reg)) {
            envmap[cm[1]] = cm[2];
        }
    }

    return envmap;
}

static json get_cfg(const char* file)
{
    std::ifstream f(file);

    try {
        return json::parse(f);
    } catch (nlohmann::json_abi_v3_11_2::detail::parse_error& exp) {
        cout << "parse file err" << endl;
        exit(-1);
    }
    return json {};
}

static json find_cfg(json& cfgfile, string path)
{
    if (!cfgfile.contains("cfg")) {
        cout << "cfg node not found" << endl;
        exit(-1);
    }

    if (cfgfile["cfg"].contains(path)) {
        return cfgfile["cfg"][path];
    }

    for (auto it : cfgfile["cfg"].items()) {
        // find regex
        const char* prefix = "REG:";
        int         pos    = it.key().find(prefix);
        if (pos == string::npos) {
            continue;
        }
        string regexp = it.key().substr(pos + strlen(prefix));
        regex  exp(regexp);

        if (regex_match(path, exp)) {
            return it.value();
        }
    }

    return json {};
}

static int get_tool_index(json& cfg, string& path)
{
    regex regindex("==============>Device :(\\d+), Mode :(\\w+) MODE.*\n.*\n path: (\\d+-.*)");

    string cmd = cfg["tool"];
    cmd += " -l";

    auto fil = popen(cmd.data(), "r");
    char buff[4096];
    int  len = 0;
    while (1) {
        int ret = fread(buff + len, 1, sizeof(buff) - len, fil);
        if (ret <= 0) {
            break;
        }
        len += ret;
    }
    pclose(fil);
    buff[len] = 0;
    cout << "get buff = " << buff << endl;

    const char* end      = buff + len;
    char*       checkpos = buff;
    cmatch      cm;
    while (regex_search((const char*)checkpos, end, cm, regindex)) {
        printf("get dev %s , mode %s , path %s\n", cm[1].str().data(), cm[2].str().data(), cm[3].str().data());

        if (cm[2] == "ROMCODE" && cm[3] == path) {
            int index = stoi(cm[1]);
            return index;
        }
        checkpos = (char*)cm[0].second;
    }

    return -1;
}

static int upload_firmware(string tool, json& cfg, int index)
{
    string cmd = tool;
    cmd += " -r -f ";
    cmd += cfg["firmware"];
    if (cfg.contains("conf") && !cfg["conf"].is_null()) {
        cmd += " -bb_cfg ";
        cmd += cfg["conf"];
    }

    cmd += " -i " + to_string(index);

    cout << "update cmd = " << cmd << endl;
#if 1
    auto fil = popen(cmd.data(), "r");
    char buff[4096];
    int  len = 0;
    while (1) {
        int ret = fread(buff, 1, sizeof(buff), fil);
        if (ret <= 0) {
            break;
        }
        buff[ret] = 0;
        cout << buff << endl;
    }
    pclose(fil);
#endif
    cout << "upload ok" << endl;
    return 0;
}

static const char* get_cfg_path(int argc, char** argv)
{
    int         c        = 0;
    const char* cfg_path = "/usrdata/install/dev_helper/conf.json";
    while (1) {
        int                  option_index   = 0;
        static struct option long_options[] = {
            {"port", required_argument, 0, 'c'},
            { 0,     0,                 0, 0  },
        };

        c = getopt_long(argc, argv, "c:", long_options, &option_index);
        if (c == -1) {
            break;
        }

        switch (c) {
        case 'c':
            cfg_path = optarg;
            break;
        default:
            break;
        }
    }
    return cfg_path;
}

int main(int argc, char** argv, char** envp)
{
    auto maps = get_envmap(envp);

    if (maps["DEVTYPE"] != "usb_device") {
        return 0;
    }

    const char* cfg_path = get_cfg_path(argc, argv);

#if 0
    for (auto p : maps) {
        printf("get key = %s , val = %s\n", p.first.data(), p.second.data());
    }
#endif
    json cfg = get_cfg(cfg_path);
    if (!cfg.contains("tool")) {
        cout << "tool path not found" << endl;
        return -1;
    }

    int    pos  = maps["DEVPATH"].find_last_of('/');
    string path = maps["DEVPATH"].substr(pos + 1);
    cout << "usb path =" << path << endl;
    json par = find_cfg(cfg, path);

    if (!par.contains("firmware")) {
        cout << path << "firmware not found" << endl;
        return -1;
    }

    if (par.contains("conf") && par.contains("usr")) {
        try {
            string tmp_patch = merge_file(par["conf"], par["usr"]);
            par["conf"]      = tmp_patch;
            cout << "patch ok ," << par["conf"] << "+" << par["usr"] << " = " << tmp_patch << endl;
        } catch (...) {
            cout << "patch failed" << endl;
        }
    } else {
        cout << "don't use patch" << endl;
    }

    cout << "firmware = " << par["firmware"] << " config = " << par["conf"] << endl;

    int index = get_tool_index(cfg, path);
    if (index < 0) {
        cout << "can't get usb index" << endl;
        return -1;
    }
    upload_firmware(cfg["tool"], par, index);

    return 0;
}
